iT邦幫忙

2022 iThome 鐵人賽

DAY 8
0
自我挑戰組

基於自然語言處理的新聞意見提取應用開發筆記系列 第 8

[Day-08] 以 AWS Lambda 和 API Gateway 建立新聞資料 API

  • 分享至 

  • xImage
  •  

Day-08 內容

  • AWS SAM 的使用介紹(延續 Day-07 昨天的結尾)
  • 以 AWS Lambda 和 API Gateway 建立新聞資料 API
    • 什麼是 API Gateway?
    • API 應用的檔案結構
    • AWS SAM 的 template.yaml 設定
    • AWS Lambda lambda_handler()
    • 在本地端進行測試
    • 部署並測試 API

因為內容有些許連貫性,建議先閱讀昨天的文章喔([Day-07] 讓 AWS Lambda 定時執行 Python 爬蟲程式

昨天 Day-07 的發文迴響屬實讓我感到非常驚訝與感動,凌晨 12 點壓線寫完收尾後,不過出門吃個宵夜的功夫,回來就發現有超過 30 次的觀看數,觸及率比先前的每篇發文都高,也帶動了其他發文的觀看。瞬間有種努力與堅持獲得回報的欣慰感,當然還是要繼續向板上那些幾百幾千觀看的個人挑戰發文看齊。

由於昨天的最後超級匆忙,在 AWS SAM CLI 使用的介紹略顯不夠完整,所以今天先以這部分的補充作為開場,後續則帶來使用 AWS SAM 部署 AWS Lambda、API Gateway 來建立新聞資料的 API 的範例,為之後從資料庫獲取前端網頁所需的資料做準備。


AWS SAM 的使用介紹(延續 Day-07 昨天的結尾)

如果你只對「以 AWS Lambda 和 API Gateway 建立資料 API」有興趣,可以先跳過此段,有需要再回來查看。

先查看一下 AWS SAM CLI(Command line interface)的基本用法,在終端機輸入指令 sam --help

❯ sam --help
Usage: sam [OPTIONS] COMMAND [ARGS]...

  AWS Serverless Application Model (SAM) CLI

  The AWS Serverless Application Model extends AWS
  CloudFormation to provide a simplified way of defining the
  Amazon API Gateway APIs, AWS Lambda functions, and Amazon
  DynamoDB tables needed by your serverless application. You
  can find more in-depth guide about the SAM specification
  here: https://github.com/awslabs/serverless-application-
  model.

Options:
  --debug     Turn on debug logging to print debug message
              generated by SAM CLI and display timestamps.

  --version   Show the version and exit.
  --info
  -h, --help  Show this message and exit.

Commands:
  init      Init an AWS SAM application.
  validate  Validate an AWS SAM template.
  build     Build your Lambda function code
  local     Run your Serverless application locally for quick...
  package   Package an AWS SAM application.
  deploy    Deploy an AWS SAM application.
  delete    Delete an AWS SAM application and the artifacts
            created by sam deploy.

  logs      Fetch logs for a function
  publish   Publish a packaged AWS SAM template to the AWS
            Serverless Application Repository.

  traces    Fetch AWS X-Ray traces
  sync      Sync a project to AWS
  pipeline  Manage the continuous delivery of the application

下方整理部分擷取自 AWS SAM CLI command reference

[OPTIONS]

sam [OPTIONS] COMMAND [ARGS]...[OPTIONS] 可選用下列選項(無選用也可)

  • --debug
    • 會在終端機中顯示包含 timestamps 的 debug 訊息(目前執行的 sam 指令的)。
  • --version
    • 印出像是 SAM CLI, version 1.55.0 的版本資訊。
  • --info
    • 印出像是 {"version": "1.55.0"} ,包含版本資訊的 json。
  • -h--help
    • 要出上一段中的一長串說明。

COMMAND [ARGS]...

sam [OPTIONS] COMMAND [ARGS]...COMMAND 可選用下列選項

init(只有應用一開始初始化使用)

  • sam init [ARGS]... 用來依照 SAM template (e.g. template.ymal)初始化無伺服器應用程式,建 Lambda 函數的資料夾結構,連接到 event source(例如 API、S3 Buckets 或 DynamoDB Table)。

  • 我推薦使用 sam init 不加任何 [ARGS]... ,進行互動式的設定操作(Day-07 昨天有範例)。

  • [ARGS]... 的用法可以使用 sam init --help 查看。

validate(常用)

  • sam validate [OPTIONS] 驗證 AWS SAM 模板檔案有效的同時,也會檢查 AWS config 和要部署檔案的區域(例如 us-east-1)等資訊。

  • 沒特別需求使用 sam validate 就可以了。

  • [OPTIONS] 的用法可以參考 sam validate

build(常用)

  • sam build [OPTIONS] [RESOURCE_LOGICAL_ID] 使用此命令構建(build)的 AWS Lambda 函數程式碼,並產生針對 AWS Lambda 執行環境的工件。

  • 可以針對指定的 Lambda 進行建構:

    To build the 'MyFunction' resource
    $ sam build MyFunction
    
    To build the 'MyFunction' resource of the 'MyNestedStack' nested stack
    $ sam build MyNestedStack/MyFunction
    
  • 像是 Day-07 昨天範例中使用 Image 的 Lambda 函數,在建構時要先啟用 Docker,然後接著執行 sam build 就可以了。

  • [OPTIONS] 的用法可以參考 sam Build

local(常用)

  • sam local [OPTIONS] COMMAND [ARGS]... 用來在本地端執行無伺服器應用程式,給有快速部署其測試需求使用,又近一步細分為下面幾個主要功能:

    • sam local generate-event [OPTIONS] COMMAND [ARGS]...

      • 從不同的事件源(如 Amazon S3、Amazon API Gateway 和 Amazon SNS)產生(在終端機印出來)有效負載範例。這些負載範例包含發送到 Lambda 函數的事件源信息(lambda_hander(context, event)event 輸入)。

      • 可用的事件源可以執行 sam local generate-event --help 查詢,

      • 其餘細節可在 sam local generate-event 中查找。

    • sam local invoke [OPTIONS] [FUNCTION_LOGICAL_ID]

      • 用來呼叫本地 AWS Lambda 函數一次,並在調用完成後退出。

      • 搭配 -e, --event EVENT_JSON_PATH 可以採用 EVENT_JSON_PATH 這個位置的 json 檔案內容作為 lambda_hander(context, event)event 輸入,相當實用。

      • 其餘細節可在 sam local invoke 中查找。

    • sam local start-api [OPTIONS]

      • 在本地運行無服務器應用程序,以便快速開發和測試。當您在包含無服務器函數和AWS SAM範本,它會創建託管您所有函式的本機 HTTP 服務器。
      • 其餘細節可在 sam local start-api 中查找。
    • sam local start-lambda [OPTIONS]

      • 以編程方式在本地調用 Lambda 函數,方法是使用AWS CLI或軟體開發套件。此命令啟動一個本地終端節點,該終端節點模擬 AWS Lambda。
      • 範例:
        # SETUP
        # ------
        # Start the local Lambda endpoint by running this command in the directory that contains your AWS SAM template.
        
        sam local start-lambda
        
        # USING AWS CLI
        # -------------
        # Then, you can invoke your Lambda function locally using the AWS CLI
        
        aws lambda invoke --function-name "HelloWorldFunction" --endpoint-url "http://127.0.0.1:3001" --no-verify-ssl out.txt
        
      • 其餘細節可在 sam local start-lambda 中查找。

package

  • sam package [OPTIONS] [ARGS]... 用以封裝 AWS SAM 應用程式。此命令會建立.zip檔案,然後將檔案上傳至 Amazon Simple Storage Service (Amazon S3)。

  • 如果跟昨天的範例一樣選用 Image 而不是 zip 則不會用到。對我來說不常用。

  • 其餘細節可在 sam package 中查找。

deploy(常用)

  • 用來部署此 AWS SAM 應用程式.

  • 在預設的情況下,當使用此命令時,AWS SAM CLI 會假定當前工作目錄是項目的根目錄。所以此 AWS SAM CLI 首先嘗試找到使用 sam build 命令時建立,位於 .aws-sam 資料夾,並命名為 template.yaml 或者template.yml 的檔案。接下來,AWS SAMCLI 嘗試找到名為 template.yaml 或者 template.yml 在目前的工作目錄。如果指定--template選項,AWS SAMCLI 的默認行為被覆蓋,並將加載 AWS SAM 模板及其指向的本地資源。最後依照指定的 template.yaml 進行資源部署。

  • 首次部署建議使用 sam deploy --guided 的互動模式,過程中會有個選項將部署選項紀錄在 samconfig.toml,第二次後的部署就可以直接採用 sam deploy,部署設定將會自動從 samconfig.toml 導入。

  • 其餘細節可在 sam deploy 中查找。

delete(實用)

  • sam delete [OPTIONS] 用來移除所有此無伺服器應用在 AWS 上的資源。

  • 如果需要輸入 --stack-name 可以在 AWS CloudFormation > 堆疊 的網頁管理頁面中找到堆疊名稱。

  • 其餘細節可在 sam deploy 中查找。

logs

  • sam logs [OPTIONS] 獲取由 Lambda 函數生成的日誌(logging)。

  • 我建議直接在網頁版的 AWS 上查會比印在終端機中好閱讀。

  • 其餘細節可在 sam log 中查找。

publish

  • sam publish [OPTIONS] 指令會發佈 AWS SAM 應用程式,並添加到 AWS Serverless Application Repository。採用一個打包 AWS SAM 範本,並將應用程式發佈至指定的AWS區域。

  • 我通常直接使用 sam deploy

  • 其餘細節可在 sam publish 中查找。

traces

  • sam traces [OPTIONS] 指令用來獲得 AWS 帳號所處區域的 AWS X-Ray 追蹤結果。

  • AWS X-Ray 服務可協助開發人員分析和偵錯生產、分散式應用程式,例如那些使用微型服務架構的建置成果。採用 X-Ray,可以了解應用程式及其基礎服務的執行方式,以識別和疑難排解效能問題與錯誤的根本原因。(參考自:AWS X-Ray

  • 其餘細節可在 sam traces 中查找。

sync

  • sam sync [OPTIONS] 會將本地更改部署到 AWS 雲端。使用sync在您對應用程序進行構建、打包和部署更改到開發環境。

  • 我通常直接使用 sam deploy,將本地更改部署到 AWS 雲端。

  • 其餘細節可在 sam sync 中查找。

pipeline(實用)

  • 使用 sam pipeline init 建立 GitHub Action CI/CD 的範例:
    ❯ sam pipeline init
    
    sam pipeline init generates a pipeline configuration file that your CI/CD system
    can use to deploy serverless applications using AWS SAM.
    We will guide you through the process to bootstrap resources for each stage,
    then walk through the details necessary for creating the pipeline config file.
    
    Please ensure you are in the root folder of your SAM application before you begin.
    
    Select a pipeline template to get started:
            1 - AWS Quick Start Pipeline Templates
            2 - Custom Pipeline Template Location
    Choice: 1
    
    Cloning from https://github.com/aws/aws-sam-cli-pipeline-init-templates.git (process may take a moment)
    Select CI/CD system
            1 - Jenkins
            2 - GitLab CI/CD
            3 - GitHub Actions
            4 - Bitbucket Pipelines
            5 - AWS CodePipeline
    Choice: 3
    You are using the 2-stage pipeline template.
     _________    _________ 
    |         |  |         |
    | Stage 1 |->| Stage 2 |
    |_________|  |_________|
    
    Checking for existing stages...
    
    [!] None detected in this account.
    
    To set up stage(s), please quit the process using Ctrl+C and use one of the following commands:
    sam pipeline init --bootstrap       To be guided through the stage and config file creation process.
    sam pipeline bootstrap              To specify details for an individual stage.
    
    ...
    後續需要填寫一些資訊
    ...
    
  • 其餘細節可在 sam pipeline init 中查找。

以 AWS Lambda 和 API Gateway 建立新聞資料 API

許多網頁會透過呼叫 API 來獲得回傳的資訊,再將這些資訊經過適當分析處理後顯示在網頁上。舉例來說如果你呼叫以下 WorldTimeAPI(可以貼在瀏覽器網址欄位測試):

http://worldtimeapi.org/api/timezone/Asia/Taipei

就可以取得如下的台北時間資訊:

{"abbreviation":"CST","client_ip":"218.164.5.168","datetime":"2022-09-23T21:55:37.140822+08:00","day_of_week":5,"day_of_year":266,"dst":false,"dst_from":null,"dst_offset":0,"dst_until":null,"raw_offset":28800,"timezone":"Asia/Taipei","unixtime":1663941337,"utc_datetime":"2022-09-23T13:55:37.140822+00:00","utc_offset":"+08:00","week_number":38}

呼叫的 API(http://worldtimeapi.org/api/timezone/Asia/Taipei)當中的路徑 /Asia/Taipei 代表以 /:area/:location 格式作為路徑的兩個參數,所以如果待換成 Europe/London 則可以獲取倫敦的時間。

現在要為我的「基於自然語言處理的新聞意見提取應用」建一個能從建立好的 PostgreSQL 資料庫取得新聞資料的 API。這裡先實作獲取新聞資料的 API,此 API 會具備以下格式的 URL:

https://.../PATH

而當中的 PATH 則需要自己根據功能來設計,我為獲取新聞資料這個功能設計了對應的路徑結構,如下方所示:

  • /list
    • /chinatime
    • /cna

使用 https://.../list (PATH=/list) 呼叫 API 時,會得到來自所有媒體的新聞資料。而使用 https://.../list/chinatimes(PATH=/list/chinatimes)呼叫 API ,則只會得到來自中時新聞網的新聞資料,https://.../list/cna(PATH=/list/cna)擇取得中央社新聞資料。

接下來將以上面「從建立好的 PostgreSQL 資料庫取得新聞資料」的想法,示範以 AWS Lambda 和 API Gateway 建立新聞資料 API。

什麼是 API Gateway?

由於會用到 AWS Lambda 和 API Gateway 兩項服務來建立新聞資料 API,下面整理了還沒介紹過的 AWS API Gateway 服務介紹:

擷取自 Amazon API Gateway
Amazon API Gateway 是一種全受管的服務,可讓開發人員輕鬆地建立、發佈、維護、監控和保護任何規模的 API。API 可作為應用程式的「前門」,以便從後端服務存取資料、商業邏輯或功能。使用 API Gateway 時,您可以建立 RESTful API 和 WebSocket API,以啟用即時雙向通訊應用程式。API Gateway 支援容器化、無伺服器工作負載和 Web 應用程式。
API Gateway 負責處理有關接受和處理多達數十萬個並行 API 呼叫的所有工作,包括流量管理、CORS 支援、授權和存取控制、調節、監控和 API 版本管理。


API 應用的檔案結構

首先可以透過與昨天相同的做法,執行 sam init ,建立一個範例的檔案結構,再將其做修改,過程如下:

❯ sam init

You can preselect a particular runtime or package type when using the `sam init` experience.
Call `sam init --help` to learn more.

Which template source would you like to use?
        1 - AWS Quick Start Templates
        2 - Custom Template Location
Choice: 1

Choose an AWS Quick Start application template
        1 - Hello World Example
        2 - Multi-step workflow
        3 - Serverless API
        4 - Scheduled task
        5 - Standalone function
        6 - Data processing
        7 - Infrastructure event management
        8 - Lambda EFS example
        9 - Machine Learning
Template: 1

Use the most popular runtime and package type? (Python and zip) [y/N]: N

Which runtime would you like to use?
        1 - dotnet6
        2 - dotnet5.0
        3 - dotnetcore3.1
        4 - go1.x
        5 - graalvm.java11 (provided.al2)
        6 - graalvm.java17 (provided.al2)
        7 - java11
        8 - java8.al2
        9 - java8
        10 - nodejs16.x
        11 - nodejs14.x
        12 - nodejs12.x
        13 - python3.9
        14 - python3.8
        15 - python3.7
        16 - python3.6
        17 - ruby2.7
        18 - rust (provided.al2)
Runtime: 13

What package type would you like to use?
        1 - Zip
        2 - Image
Package type: 2

Based on your selections, the only dependency manager available is pip.
We will proceed copying the template using pip.

Would you like to enable X-Ray tracing on the function(s) in your application?  [y/N]: N

Project name [sam-app]:

注意:Project name [sam-app]: 可以按 enter 使用預設的 sam-app 或是自行命名。

完成後獲得到如下的檔案結構:

❯ tree
.
└── sam-app
    ├── README.md
    ├── __init__.py
    ├── events
    │   └── event.json
    ├── hello_world
    │   ├── Dockerfile
    │   ├── __init__.py
    │   ├── app.py
    │   └── requirements.txt
    ├── template.yaml
    └── tests
        ├── __init__.py
        └── unit
            ├── __init__.py
            └── test_handler.py

5 directories, 11 files

這次的 API 應用則要針對 template.yamlapp.pyrequirements.txt 進行更改。


AWS SAM 的template.yaml 設定

將上面檔案結構中的 template.yaml 替換成下面的設定:

AWSTemplateFormatVersion: '2010-09-09'
Transform: AWS::Serverless-2016-10-31
Description: >
  python3.9

  Sample SAM Template for database-api

# More info about Globals: https://github.com/awslabs/serverless-application-model/blob/master/docs/globals.rst
Globals:
  Function:
    Timeout: 60

Resources:
  HelloWorldFunction:
    Type: AWS::Serverless::Function
    Properties:
      PackageType: Image
      Architectures:
        - x86_64
      Events:
        GetAll:
          Type: Api
          Properties:
            Path: /all
            Method: get
        GetChinatimes:
          Type: Api # More info about API Event Source: 
          Properties:
            Path: /chinatimes
            Method: get
        GetCna:
          Type: Api # More info about API Event Source: 
          Properties:
            Path: /cna
            Method: get
        
     Policies: AWSLambdaVPCAccessExecutionRole
     VpcConfig:
       SecurityGroupIds:
         - sg-XXXX
       SubnetIds:
         - subnet-XXXX
    Metadata:
      Dockerfile: Dockerfile
      DockerContext: ./hello_world
      DockerTag: python3.9-v1

當中可以觀察到,我將觸發 Lambda 函數的事件(Events)換成多個 API,說明如下

Events: # 觸發Lambda 函數的事件(Events)
    GetAll: # 用來獲取所有媒體新聞資料的 API
        Type: Api
        Properties:
            Path: /list # 此函數被調用的 Uri 路徑
            Method: get # 採用 GET HTTP 方法調用此函數
    GetChinatime:s # 用來獲取中時新網新聞資料的 API
        Type: Api
        Properties:
            Path: /list/chinatimes # 此函數被調用的 Uri 路徑
            Method: get # 採用 GET HTTP 方法調用此函數
    GetCna: # 用來獲取中央社新聞資料的 API
        Type: Api
        Properties:
            Path: /list/cna # 此函數被調用的 Uri 路徑
            Method: get # 採用 GET HTTP 方法調用此函數

上面是我針對示範的任務所建立的配置,如果要針對自己的應用進行調整,可以參考 Api

下方片段則按照 [Day-07] 讓 AWS Lambda 定時執行 Python 爬蟲程式中的介紹,填入與 PostgreSQL 資料庫相同的 VPC 設定。

Policies: AWSLambdaVPCAccessExecutionRole
VpcConfig:
    SecurityGroupIds:
         - sg-XXXX
    SubnetIds:
         - subnet-XXXX

AWS Lambda lambda_handler()

由於這個 Lambda 應用程式需要使用 Python 連線到前幾天建立的 PostgreSQL 資料庫,在 [Day-06] 以 PostgreSQL 建立新聞資料庫(採用 AWS Amazon Aurora Serverless v2) 有提到會需要 psycopg 這個套件。所以先將 psycopg 紀錄在 requirements.txt 當中,依照 Dockerfile (維持 sam init 後的 Dockerfile)中所設定的流程, psycopg 會被安裝。

psycopg 紀錄在 requirements.txt 當中(注意要採用二進制版本):

psycopg[binary]

接著將 app.py 替換成下列程式碼:

import json
import psycopg
from psycopg.rows import dict_row


def lambda_handler(event, context):
    
    path = event["path"]

    conninfo = f"host={DB_INSTANCE_ENDPOINT} dbname={DB_NAME} port={PORT} user={MASTER_USER_NAME} password={PASSWORD}"
    with psycopg.connect(conninfo) as conn:
        # Open a cursor to perform database operations

        with conn.cursor(row_factory=dict_row) as cur:

            # Query the database and obtain data as Python objects.
            if path == "/list":
                cur.execute("SELECT * FROM newscollecttest ORDER BY time DESC LIMIT 20")
            
            elif path == "/list/chinatimes":
                cur.execute("SELECT * FROM newscollecttest WHERE source='中時新聞網' ORDER BY time DESC LIMIT 20")
            
            elif path == "/list/cna":
                cur.execute("SELECT * FROM newscollecttest WHERE source = '中央社' ORDER BY time DESC LIMIT 20")
         

            result = cur.fetchall()
            print(result)

            return {
                "statusCode": 200,
                "headers": {
                    'Access-Control-Allow-Headers': 'Content-Type',
                    'Access-Control-Allow-Origin': '*',
                    'Access-Control-Allow-Methods': 'GET'
                },
                "body": json.dumps(
                    {
                        "data": result,
                    },
                    default=str,
                    ensure_ascii=False
                ),
            }

上面的程式碼中先透過 path = event["path"] 來取得被呼叫的 API 的路徑(e.g. /list/cna),配合 if 流程去執行對應的 SQL 取得 20 筆新聞資料,再藉由 result = cur.fetchall() 或得回傳的每筆資料(注意有搭配 from psycopg.rows import dict_row 一併使用)。

另外,當中的 conninfo = f"host={DB_INSTANCE_ENDPOINT} dbname={DB_NAME} port={PORT} user={MASTER_USER_NAME} password={PASSWORD}" 需要依照 [Day-06] 以 PostgreSQL 建立新聞資料庫(採用 AWS Amazon Aurora Serverless v2) 中的說明,換成資料庫連線所需的資訊。

return {
    "statusCode": 200,
    "headers": {
        'Access-Control-Allow-Headers': 'Content-Type',
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET'
    },
    "body": json.dumps(
        {
            "data": result,
        },
        default=str,
        ensure_ascii=False
    ),
}

這部分會作為 API 的 response,"statusCode": 200 表示成功,此處 headers 的設定能解決在呼叫 API 遇到的 CORS 錯誤(參考 如何對 API Gateway API 的 CORS 錯誤進行疑難排解?)。"body" 則包含了 json 格式的回傳資料,使用 json.dumps 搭配 ensure_ascii=False 是為了避免將中文轉成 ascii 的顯示方式。


在本地端進行測試

  1. sam-app/events/event.json 的內容改成下面的 json:

    {
        "path": "/list"
    }
    

    換成 /list/chinatimes/list/chinatimes 也可以測試。

  2. 運行(run)Docker。

  3. 執行 sam build

  4. 最後執行 sam local invoke "HelloWorldFunction" --event ./events/event.json 就可以看到 API 的回傳結果顯示在終端機中(因為有加 print(result)app.py)。

    注意 "HelloWorldFunction" 是根據 template.yaml 中的定義。


部署並測試 API

  1. 首次部署建議使用 sam deploy --guided 的互動模式,過程中會有個選項將部署選項紀錄在 samconfig.toml,第二次後的部署就可以直接採用 sam deploy,部署設定將會自動從 samconfig.toml 導入。

  2. 根據 在 Amazon API Gateway 中叫用 REST API 中的指示,獲得具備以下格式的 URL:

    https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/
    
  3. https://{restapi_id}.execute-api.{region}.amazonaws.com/{stage_name}/ 格式的末端加上你設計的 PATH (例如 /list/cna),接著整串貼到瀏覽器網址欄,按 enter。

  4. API 的回傳結果就會顯示在瀏覽器當中。


終於完成了!/images/emoticon/emoticon56.gif
寫的有些匆忙,如果文章有錯誤,歡迎指正~ /images/emoticon/emoticon41.gif


上一篇
[Day-07] 讓 AWS Lambda 定時執行 Python 爬蟲程式
下一篇
[Day-09] 從政治新聞中提取人物意見的方法構想
系列文
基於自然語言處理的新聞意見提取應用開發筆記17
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言